home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / SWAG / SWAGA_C / COMM.SWG / 0070_Serial Communications in Pascal.pas < prev    next >
Pascal/Delphi Source File  |  1995-03-03  |  12KB  |  434 lines

  1.  
  2. {
  3. >     Ok... I have fifty million different modem units, sources, docs
  4. > I have not been able to get any where with any of them....  All i want
  5.  
  6. I remember being in your shoes a long time ago, my friend.  I haven't
  7. a Unit, per se, but here is _WORKING_ Pascal source (excuse the
  8. documentation style, I originally wrote the code in C) that compiles under
  9. TP 6.0.
  10.  
  11.   Written by Dave Jarvis.
  12.   
  13.   The purpose of this program is to do a simple communications protocol and 
  14.   demonstrate how Serial communications can be Serially driven. 
  15.  
  16. USES DOS, Crt; 
  17.  
  18. CONST 
  19.   COM1      = $3F8;    { Communications port 1 address.     } 
  20.   COM2      = $2F8;    { Communications port 2 address.     } 
  21.   THR       = $00;     { Transmitter holding register.      } 
  22.   RDR       = $00;     { Receiver data register.            } 
  23.   BRDL      = $00;     { Baud rate low divisor register.    } 
  24.   BRDH      = $01;     { Baud rate high divisor register.   } 
  25.   IER       = $01;     { Interrupt enable register.         } 
  26.   IIR       = $02;     { Interrupt identIFication register. } 
  27.   LCR       = $03;     { Line control register.             } 
  28.   MCR       = $04;     { Modem control register.            } 
  29.   LSR       = $05;     { Line status register.              } 
  30.   MSR       = $06;     { Modem status register.             } 
  31.  
  32.   SET_BAUD  = $80;     { DLAB.                              } 
  33.   CTS_DSR   = $30;     { Check for DSR and CTS in MSR.      } 
  34.   THREMPTY  = $20;     { Check for THR empty in LSR.        } 
  35.  
  36.   WORD7      = $02;    { Bits 0 and 1 when setting LCR.     } 
  37.   WORD8      = $03; 
  38.   BIT1       = $00;    { Bit 3 when setting LCR.            } 
  39.   BIT2       = $04; 
  40.   NONE       = $00;    { Bits 4, 5, and 6 when setting LCR. } 
  41.   EVEN       = $30; 
  42.   ODD        = $20; 
  43.  
  44.   INT_ENABL  = $0B;    { Tell UART to perform interrupts.   } 
  45.   PIC        = $20;    { Address of PIC.                    } 
  46.   PIC_CNTL   = $21;    { Address of PIC control register.   } 
  47.   IRQ4_MASK  = $EF;    { Mask for IRQ4.                     } 
  48.   COM_INT    = $0C;    { Communications interrupt to tap.   } 
  49.   EOI        = $20;    { End of interrupt signal for PIC.   } 
  50.   DATA_REC   = $01;    { Interrupt on data received.        } 
  51.   WRITE_CH   = $0E;    { Write character function.          } 
  52.  
  53.   XON_CH     = #$11;   { XON protocol control character.    } 
  54.   XOFF_CH    = #$13;   { XOFF protocol control character.   } 
  55.   EOT        = #$04;   { End of transmission character.     }
  56.  
  57.   MAX_BUFF   = 256;     { Maximum characters in the buffer.  }
  58.   BUFF_FULL  = 0.75;    { Buffer full @ 75% of MAX_BUFF.     }
  59.   BUFF_EMPT  = 0.50;    { Buffer empty @ 50% of MAX_BUFF.    }
  60.  
  61.   ERR        = -1;
  62.  
  63. TYPE
  64.   RecLinkPtr = ^Receive;
  65.  
  66.   Receive = RECORD
  67.               rec_char : CHAR;
  68.               Next     : RecLinkPtr;
  69.             End;
  70.  
  71. VAR
  72.   rec_buff,                  { Global linked list. } 
  73.   to_write   : RecLinkPtr; 
  74.   buff_Count : INTEGER;      { Number of characters in buffer.    } 
  75.   xon        : BOOLEAN;      { Enable send ability.               } 
  76.  
  77. PROCEDURE ShowUsage; 
  78. Begin 
  79.    WriteLn( 'Usage : TERMINAL <baud> <parity> <data bits> <stop bits>' ); 
  80.    WriteLn( 'Where : <baud> is any of 300, 1200, 2400, 9600;' ); 
  81.    WriteLn( '        <parity> is any of N, O, E;' ); 
  82.    WriteLn( '        <data bits> is either 7 or 8;' ); 
  83.    WriteLn( '        <stop bits> is either 1 or 2.' ); 
  84.  
  85.    Halt( 0 );
  86.  
  87. End; 
  88.  
  89. PROCEDURE setup( baud, parity, data_bits, stop_bits : INTEGER ); 
  90. VAR 
  91.   setup : INTEGER; 
  92.  
  93. Begin 
  94.   setup := parity;
  95.  
  96.  
  97.   { Set DLAB such that baud rate can be changed/set. } 
  98.   Port[ COM1 + LCR ] := SET_BAUD; 
  99.  
  100.   CASE baud OF 
  101.      300 : Begin 
  102.              Port[ COM1 + BRDL ] := $80; 
  103.              Port[ COM1 + BRDH ] := $01; 
  104.            End; 
  105.     1200 : Begin 
  106.              Port[ COM1 + BRDL ] := $60; 
  107.              Port[ COM1 + BRDH ] := $00; 
  108.            End; 
  109.     2400 : Begin 
  110.              Port[ COM1 + BRDL ] := $30; 
  111.              Port[ COM1 + BRDH ] := $00; 
  112.            End; 
  113.     9600 : Begin 
  114.              Port[ COM1 + BRDL ] := $0C; 
  115.              Port[ COM1 + BRDH ] := $00; 
  116.            End;
  117.     ELSE
  118.       ShowUsage;
  119.   End;
  120.  
  121.   CASE data_bits OF
  122.     7  : setup := setup OR WORD7;
  123.     8  : setup := setup OR WORD8;
  124.     ELSE
  125.       ShowUsage;
  126.   End;
  127.  
  128.   CASE stop_bits OF
  129.     1  : setup := setup OR BIT1;
  130.     2  : setup := setup OR BIT2;
  131.     ELSE
  132.       ShowUsage;
  133.   End;
  134.  
  135.   { Send final (calculated) setup Value to the communications port. }
  136.   Port[ COM1 + LCR ] := setup;
  137. End;
  138.  
  139. PROCEDURE add_char( ch : CHAR );
  140. Begin 
  141.   { IF the buffer is full, then sound the speaker twice -- toss char. } 
  142.   IF( buff_Count = MAX_BUFF ) THEN 
  143.   Begin 
  144.     Sound( 1000 ); 
  145.     Sound( 900 ); 
  146.     NoSound; 
  147.  
  148.     Exit; 
  149.   End; 
  150.  
  151.   { Store character in buffer. } 
  152.   rec_buff^.rec_char := ch; 
  153.  
  154.   { Point to next storage position. } 
  155.   rec_buff := rec_buff^.next; 
  156.  
  157.   { Increment number of characters in buffer. } 
  158.   INC( buff_Count ); 
  159. End; 
  160.  
  161. {$F+} 
  162. PROCEDURE receive_ch; INTERRUPT; 
  163. VAR 
  164.   ch : CHAR; 
  165.  
  166. Begin 
  167.   ch := CHAR(Port[ COM1 + RDR ]); 
  168.  
  169.   IF( ch = XON_CH ) THEN 
  170.     xon := TRUE 
  171.   ELSE IF( ch = XOFF_CH ) THEN 
  172.     xon := FALSE 
  173.   ELSE 
  174.     add_char( CHAR(ch) ); 
  175.  
  176.   { Send End of interrupt signal to PIC chip. } 
  177.   Port[ PIC ] := EOI; 
  178. End; 
  179. {$F-} 
  180.  
  181. PROCEDURE xmit( ch : CHAR ); 
  182. Begin 
  183.   Port[ COM1 + THR ] := INTEGER(ch); 
  184. End;
  185.  
  186. FUNCTION can_xmit : BOOLEAN;
  187. Begin
  188.   {  IF input characters can be sent, and the DSR, CTS and THREMPTY are
  189.      all set high, then the character read from keyboard can be sent.
  190.   }
  191.   IF( xon AND ((Port[ COM1 + MSR ] AND CTS_DSR)  = CTS_DSR) AND
  192.              ((Port[ COM1 + LSR ] AND THREMPTY) = THREMPTY) ) THEN
  193.     can_xmit := TRUE
  194.   ELSE
  195.     can_xmit := FALSE;
  196.  
  197. End;
  198.  
  199. PROCEDURE writech;
  200. Begin
  201.   Write( to_write^.rec_char );
  202.  
  203.   { Decrement the number of actual elements in the buffer. }
  204.   DEC( buff_Count );
  205.  
  206.   { Point to the next character to write (IF any are left). }
  207.   to_write := to_write^.next;
  208. End;
  209.  
  210. PROCEDURE send_string( s : STRING );
  211. VAR 
  212.   Count : INTEGER; 
  213.  
  214. Begin 
  215.   FOR Count := 1 TO Length( S ) DO 
  216.   Begin 
  217.     WHILE( NOT can_xmit ) DO 
  218.       ; 
  219.  
  220.     xmit( s[ Count ] ); 
  221.   End; 
  222. End; 
  223.  
  224. PROCEDURE Serial; 
  225. VAR 
  226.   ch       : CHAR;       { Character read from the keyboard.    } 
  227.   done,                  { Loop until done = TRUE.              } 
  228.   send_xon : BOOLEAN;    { TRUE IF XON character has been sent. } 
  229.  
  230. Begin 
  231.   done     := FALSE; 
  232.   send_xon := TRUE; 
  233.  
  234.   Repeat 
  235.     {  IF a character is in the keyboard buffer, and it can be sent to the 
  236.        UART, then read it from the keyboard buffer, and transmit it. 
  237.     } 
  238.     IF( can_xmit AND KeyPressed ) THEN 
  239.     Begin 
  240.       ch := ReadKey; 
  241.  
  242.       IF( ch = EOT ) THEN 
  243.         done := TRUE 
  244.       ELSE 
  245.         xmit( ch ); 
  246.     End; 
  247.  
  248.     IF( buff_Count > 0 ) THEN 
  249.     Begin 
  250.       { Display a character from the buffer. } 
  251.       writech; 
  252.  
  253.       { IF the buffer is more than 75% full, then send XOFF char ASAP. } 
  254.       IF( (buff_Count / (MAX_BUFF * 1.0)) > BUFF_FULL ) THEN 
  255.       Begin
  256.         { Wait until a character can be sent. } 
  257.         WHILE( NOT can_xmit ) DO 
  258.           ; 
  259.  
  260.         { Send the XOFF control code. } 
  261.         xmit( XOFF_CH ); 
  262.  
  263.         { Indicate that an XON can be sent anytime. } 
  264.         send_xon := FALSE; 
  265.       End; 
  266.  
  267.       { IF the buffer is less than 50% full, then send XON char ASAP. } 
  268.       IF( ((buff_Count / (MAX_BUFF * 1.0)) < BUFF_EMPT) AND 
  269.            (NOT send_xon) ) THEN 
  270.       Begin 
  271.         { Wait until a character can be sent. } 
  272.         WHILE( NOT can_xmit ) DO 
  273.           ; 
  274.  
  275.         { Send the XON control code. } 
  276.         xmit( XON_CH ); 
  277.  
  278.         { An XON control code has been sent. } 
  279.         send_xon := TRUE; 
  280.       End; 
  281.     End; 
  282.   Until( done ); 
  283. End; 
  284.  
  285. FUNCTION Value( NumS : STRING ) : LONGINT;
  286. VAR 
  287.   O, M, S, C : LONGINT; 
  288.  
  289. Begin 
  290.   S := 0; 
  291.   M := 1; 
  292.  
  293.   FOR C := Length(NumS) DOWNTO 1 DO 
  294.   Begin 
  295.     O := Ord( NumS[C] ); 
  296.  
  297.     IF NumS[C] IN ['0'..'9'] THEN 
  298.     Begin 
  299.       INC( S, M * (O - 48) ); 
  300.       M := M * 10; 
  301.     End; 
  302.   End; 
  303.  
  304.   Value := S; 
  305. End; 
  306.  
  307. FUNCTION UCase( S : STRING ) : STRING; 
  308. VAR 
  309.   C : BYTE; 
  310.  
  311. Begin 
  312.   FOR C := 1 TO Length(S) DO 
  313.     S[C] := UpCase( S[C] ); 
  314.  
  315.   UCase := S; 
  316. End; 
  317.  
  318. VAR 
  319.   Count,                { Simple Counter.                    } 
  320.   baud,                 { Baud rate.                         } 
  321.   parity,               { Parity    - NONE, EVEN, ODD.       } 
  322.   data_bits,            { Data bits - 7, 8.                  } 
  323.   stop_bits  : INTEGER; { Stop bits - 1, 2.                  } 
  324.   temp, 
  325.   current    : RecLinkPtr; 
  326.   SecParam   : STRING; 
  327.   save_int   : POINTER; 
  328.  
  329. Begin 
  330.   buff_Count := 0;
  331.   xon        := TRUE;
  332.  
  333.   { 4 command line arguments (include program name) are required. }
  334.   IF( ParamCount <> 4 ) THEN
  335.     ShowUsage;
  336.  
  337.   { The first command line argument is specified to be the baud rate. }
  338.   baud := Value( ParamStr( 1 ) );
  339.  
  340.   { Convert second argument to upper case so first letter can be checked
  341.     for parity. }
  342.   SecParam := ParamStr( 2 );
  343.   SecParam := UCase( SecParam );
  344.  
  345.   { Check first character of 2nd command line parameter for parity. }
  346.   CASE SecParam[1] OF
  347.     'N' : parity := NONE;
  348.     'O' : parity := ODD;
  349.     'E' : parity := EVEN;
  350.   ELSE
  351.     ShowUsage;
  352.   End;
  353.  
  354.   rec_buff := NIL;
  355.   {  Allocate enough memory for MAX_BUFF characters (New is not re-entrant).
  356.   } 
  357.   FOR Count := 0 TO MAX_BUFF - 1 DO 
  358.   Begin 
  359.     New( temp ); 
  360.  
  361.     temp^.next     := NIL; 
  362.     temp^.rec_char := #0; 
  363.  
  364.     IF( rec_buff = NIL ) THEN 
  365.       rec_buff := temp 
  366.     ELSE 
  367.     Begin 
  368.       current := rec_buff; 
  369.  
  370.       WHILE( current^.next <> NIL ) DO 
  371.         current := current^.next; 
  372.  
  373.       current^.next := temp; 
  374.     End; 
  375.   End; 
  376.  
  377.   {  Create a circular buffer by pointing the last element in the list to 
  378.      the start (head) of the list. 
  379.   } 
  380.   temp^.next := rec_buff; 
  381.  
  382.   { Point to the first character to write within the buffer. } 
  383.   to_write := rec_buff; 
  384.  
  385.   data_bits := Value( ParamStr( 3 ) );
  386.  
  387.   stop_bits := Value( ParamStr( 4 ) ); 
  388.  
  389.   getintvec( COM_INT, save_int ); 
  390.  
  391.   { Set vector = $0C to new interrupt routine. } 
  392.   SetIntVec( COM_INT, Addr( receive_ch ) ); 
  393.  
  394.   { Initialize the modem according to the command line parameters. } 
  395.   setup( baud, parity, data_bits, stop_bits ); 
  396.  
  397.   { Interrupt on received character. } 
  398.   Port[ COM1 + IER ] := DATA_REC; 
  399.  
  400.   { Enable interrupts }
  401.   Port[ COM1 + MCR ] := INT_ENABL; 
  402.  
  403.   { Set PIC control register to enable IRQ4. } 
  404.   Port[ PIC_CNTL ] := Port[ PIC_CNTL ] AND IRQ4_MASK; 
  405.  
  406.   { Set MSR such that CTS and DSR are high. } 
  407.   Port[ COM1 + MSR ] := CTS_DSR; 
  408.  
  409.   ClrScr; 
  410.   WriteLn( 'Type Control-D at any time to quit.' ); 
  411.  
  412.   { Repeat Serial communications. } 
  413.   Serial; 
  414.  
  415.   { Disable interrupts } 
  416.   Port[ COM1 + IER ] := 0; 
  417.  
  418.   { Set PIC control register to disable IRQ4. } 
  419.   Port[ PIC_CNTL ] := Port[ PIC_CNTL ] AND (NOT IRQ4_MASK); 
  420.  
  421.   { Set vector = $0C to old interrupt routine. } 
  422.   SetIntVec( COM_INT, save_int ); 
  423.  
  424.   { Deallocate all the memory used in the buffer. } 
  425.   FOR Count := 0 TO MAX_BUFF - 1 DO 
  426.   Begin 
  427.     temp     := rec_buff; 
  428.     rec_buff := rec_buff^.next; 
  429.  
  430.     Dispose( temp ); 
  431.   End; 
  432. End. 
  433.